﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Group3.MediaTool.Interface;
using Group3.MediaTool.Provider;
using System.Xml.Linq;
using Group3.MediaTool.Core;
using System.Reflection;
using Group3.MediaTool.Generator;
using System.Data.SqlClient;
using System.Data;
using Group3.MediaTool.Data.IO;

namespace Group3.MediaTool.Data.Generator
{
    public class DataLoader
    {

        //	category = REF_category
        //

        String path = Group3.MediaTool.Data.IO.StaticConfig.pathDataDefinition;
        List<List<String>> references = new List<List<String>>();

        public void loadData<T>(string pStrPath, T obj) where T : IObjectRelationModel
        {
            //DataLoaderCache dlc = new DataLoaderCache();

            XDocument doc;
            List<IObjectRelationModel> result = new List<IObjectRelationModel>();

            String test = Path.Combine(pStrPath);
            if (!File.Exists(test)) { return; }

            String _className = (obj.GetType().ToString().ToUpper()).Split('.').Last();

            doc = XDocument.Load(pStrPath);

            var XMLobject = doc.Root;

            IEnumerable<XElement> objs = from c in XMLobject.Descendants()
                                         where c.Name.ToString().ToUpper() == _className
                                         select c;

            IObjectRelationModel on = (T)Activator.CreateInstance(typeof(T));
            IEnumerable<String> properties = on.GetPropertyNames();

            SQLDatabaseProvider sqlConn = new SQLDatabaseProvider();
            sqlConn.Connect = "Server=localhost;Integrated security=SSPI;database=Group3_BookTool";

            foreach (XElement o in objs)
            {
                Dictionary<String, String> data = new Dictionary<String, String>();
                String oldId = "";
                on = (T)Activator.CreateInstance(typeof(T));

                foreach (string s in properties)
                {
                    String sUpper = s.ToUpper();
                    IEnumerable<XElement> eles = from e in o.Descendants()
                                                 where e.Name.ToString().ToUpper() == sUpper
                                                 select e;
                    if (eles.Count() > 0)
                    {
                        XElement ele = eles.First();
                        if (ele != null)
                        {
                            if (s != "Id")
                            {
                                System.Reflection.PropertyInfo propInfo = typeof(T).GetProperty(s);

                                if (propInfo.PropertyType == typeof(Nullable<int>))
                                {
                                    int i;
                                    bool success = int.TryParse(ele.Value, out i);
                                    on.SetValue(s, new int?(i));
                                }

                                if (propInfo.PropertyType == typeof(System.Nullable<decimal>))
                                {
                                    decimal i;
                                    bool success = decimal.TryParse(ele.Value, out i);
                                    on.SetValue(s, new decimal?(i));
                                }

                                if (propInfo.PropertyType == typeof(String))
                                {
                                    on.SetValue(s, ele.Value.ToString());
                                }
                                var onValue = on.GetValue(s);
                                //If the property is generic it's a list and the relations have to be read as well
                                //if(Relation ....
                                if (onValue != null && ((onValue).GetType()).IsGenericType)
                                {
                                    var test2 = ele.Value;
                                }
                            }
                            else
                            {
                                oldId = ele.Value;
                            }
                        }
                        else
                        {
                            on.SetValue(s, "");
                            
                        }
                    }
                }
                bool res = sqlConn.SaveChanges(on);
                if (res)
                {
                    if (!String.IsNullOrEmpty(oldId))
                    {
                        DataLoaderCache.addMapping(_className, oldId, Convert.ToInt32(on.GetValue("Id")));
                    }
                }
                else
                {
                    //TODO
                }


                result.Add(on);

                IEnumerable<XElement> refEles = from e in o.Descendants()
                                                where e.Name.ToString().ToUpper().Contains("REF_")
                                                select e;

                foreach (XElement ele in refEles)
                {
                    String refAttrClass = ele.Name.ToString();

                    IEnumerable<XElement> arrRefTab = from refT in XMLobject.Descendants("MAPPINGS").Descendants("MAP")
                                                    where refT.Element("Attr").Value.ToString().ToUpper() == refAttrClass.ToUpper()
                                                    select refT;

                    String refClassName = arrRefTab.Elements("RefClass").First().Value;
                    String refTab = arrRefTab.Elements("RefTab").First().Value;
                    DataLoaderCache.addAssign(_className.TrimStart(("E_").ToCharArray()), oldId, refClassName, ele.Value, refTab);

                }
            }

            return;

        }

        public List<Type> loadAll(List<IObjectDefinition> ddObjects)
        {
            ClassBuilder cb = new ClassBuilder();
            DataDefinitionIO ddIO = new DataDefinitionIO();
            List<Type> types = new List<Type>();


            foreach (IObjectDefinition itm in ddObjects)
            {

                ddIO.writeLogFile("Start creating " + itm.OrmName + " at " + DateTime.Now, "Info");
                cb.BuildClass(itm);

                //The ObjectProvider loads the new generated Class into the testapplication and can now be used in it.
                ObjectProvider.Instance = new ObjectProvider();
                ObjectProvider.Instance.DoImport();
                ObjectProvider.Instance.DoReleaseExport("E_" + itm.OrmName);

                IObjectRelationModel newObj = ((IObjectRelationModel)ObjectProvider.Instance.ProvidedObjects.Where(o => o.GetType().Name == "E_" + itm.OrmName).FirstOrDefault().Instantiate());
                Type Y = newObj.GetType();
                types.Add(Y);
                //if (itm.OrmName == "Book") { B = Y; }
              
                Type Z = this.GetType();
                MethodInfo method = Z.GetMethod("loadData");
                MethodInfo genericMethod = method.MakeGenericMethod(Y);

                Object[] objArr = new Object[2];
                objArr[0] = Path.Combine(StaticConfig.pathDataDefinition, "data" + itm.OrmName + ".xml");
                objArr[1] = newObj;
                genericMethod.Invoke(this, objArr);
                ddIO.writeLogFile("Finished creating " + itm.OrmName + " at " + DateTime.Now, "Info");
            }
            ddIO.writeLogFile("Start creating references at " + DateTime.Now, "Info");
            writeReferences();
            ddIO.writeLogFile("Finished creating references at " + DateTime.Now, "Info");
            return types;
        }

        public void writeReferences()
        {
            List<IdAssign> helper = DataLoaderCache.assignHelper;
            SQLDatabaseProvider sqlCon = new SQLDatabaseProvider();

            foreach (IdAssign ass in helper)
            {
                IEnumerable<int> thisClassOb = from cls in DataLoaderCache.loadHelper
                                               where (cls.classname == "E_" + ass.thisClassRef &&
                                               cls.mapping.Key == ass.thisClassId)
                                               select cls.mapping.Value;
                int thisClassId = -1;
                if (thisClassOb != null && thisClassOb.Count() > 0 )
                {
                    thisClassId = thisClassOb.First();
                }

                IEnumerable<int> otherClassOb = from cls in DataLoaderCache.loadHelper
                                                where (cls.classname == "E_" + ass.otherClassRef &&
                                                     cls.mapping.Key == ass.otherClassId)
                                                select cls.mapping.Value;

                int otherClassId = -1;
                if (otherClassOb != null && otherClassOb.Count() > 0)
                {
                    otherClassId = otherClassOb.First();
                }

                if (otherClassId == -1 || thisClassId == -1)
                {
                    DataDefinitionIO ddIO = new DataDefinitionIO();
                    ddIO.writeLogFile("No entry found for Class " + ass.thisClassRef + " with ID " + ass.thisClassId + " (" + thisClassId + ")", "Warning");
                    ddIO.writeLogFile("No entry found for Class " + ass.otherClassRef + " with ID " + ass.otherClassId + " (" + otherClassId + ")", "Warning");
                    continue;
                }

                String _sqlMultiUpdate = String.Concat("INSERT INTO ", ass.refTab, " ( ", ass.thisClassRef, ", ", ass.otherClassRef, " ) VALUES (@", ass.thisClassRef, ", @", ass.otherClassRef, ");");

                List<SqlParameter> paras = new List<SqlParameter>();

                SqlParameter sqlParam1 = new SqlParameter("@" + ass.thisClassRef, SqlDbType.BigInt);
                sqlParam1.Value = thisClassId;
                paras.Add(sqlParam1);
                SqlParameter sqlParam2 = new SqlParameter("@" + ass.otherClassRef, SqlDbType.BigInt);
                sqlParam2.Value = otherClassId;
                paras.Add(sqlParam2);

                int i;
                sqlCon.runCommand(_sqlMultiUpdate, paras, "",out i);
            }

        }

    }
}